Skip to content

speed up plugin installation with native ZIP extraction - #2715

Open
codewithchai0605 wants to merge 5 commits into
Acode-Foundation:mainfrom
codewithchai0605:feat/better-plugin-installer
Open

speed up plugin installation with native ZIP extraction#2715
codewithchai0605 wants to merge 5 commits into
Acode-Foundation:mainfrom
codewithchai0605:feat/better-plugin-installer

Conversation

@codewithchai0605

Copy link
Copy Markdown

Summary

This PR improves plugin installation performance, especially for plugins containing many small files such as file/folder icon providers.

The previous installer extracted archive entries in JavaScript with a concurrency limit of 2. This caused significant overhead from repeated JS-to-native filesystem calls.

This change moves archive extraction to native Android code and writes files directly from the ZIP archive.

Changes

  • Extract plugin ZIP archives natively using ZipFile.
  • Stream archive entries directly to a staging directory.
  • Activate updates through a directory swap, preserving the existing plugin if extraction fails.
  • Restore the previous plugin installation if an earlier update was interrupted during the swap.
  • Prevent concurrent installations of the same plugin.
  • Validate plugin IDs and archive paths to prevent path traversal.
  • Add archive size and entry-count limits.
  • Optimize extraction for many small files:
    • shared 64 KiB buffer
    • direct output streams
    • no per-entry canonical filesystem lookup

@greptile-apps

greptile-apps Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR moves plugin ZIP extraction into the native Android bridge and activates installations through staging-directory swaps.

  • Adds plugin-ID, archive-path, entry-count, and extracted-size validation.
  • Adds per-plugin extraction locking and interrupted-swap recovery.
  • Replaces incremental JavaScript extraction with cache-backed native ZipFile streaming.

Confidence Score: 4/5

The interrupted-update state misclassification should be fixed before merging because a subsequent plugin load failure can remove both the recovered installation and its backup.

Native recovery can restore a missing destination after JavaScript has recorded it as a fresh install, causing the error path to delete the plugin, while interrupted staging directories can also accumulate indefinitely.

Files Needing Attention: src/lib/installPlugin.js and src/plugins/pluginContext/src/android/Tee.java

Important Files Changed

Filename Overview
src/lib/installPlugin.js Delegates extraction to native code, but snapshots installation state before native recovery and can consequently delete a recovered update after a load failure.
src/plugins/pluginContext/src/android/Tee.java Adds bounded native ZIP extraction and swap recovery, but restart cleanup leaves interrupted staging directories orphaned.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Download and validate ZIP in JavaScript] --> B[Write ZIP to cache]
  B --> C[Invoke Tee.extractPluginArchive]
  C --> D[Restore interrupted backup]
  D --> E[Extract into staging directory]
  E --> F[Move current plugin to backup]
  F --> G[Move staging to destination]
  G --> H[Delete backup]
  H --> I[Load installed plugin]
  I -->|Load error on recovered update| J[JavaScript cleanup may delete destination]
Loading

Reviews (1): Last reviewed commit: "perf: reduce native plugin extraction ov..." | Re-trigger Greptile

Comment thread src/lib/installPlugin.js
}
assertSafePluginId(id);
pluginDir = Url.join(PLUGIN_DIR, id);
pluginWasInstalled = await fsOperation(pluginDir).exists();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Recovered update misclassified as fresh

When an interrupted update leaves only a backup, pluginWasInstalled is recorded as false before native recovery restores that backup. If the subsequent plugin load fails, the catch block treats the recovered update as a fresh installation and deletes the activated directory after its backup has been removed, leaving the plugin uninstalled.

Knowledge Base Used: Plugin System

Comment on lines +191 to +194
staging = new File(
parent,
"." + destination.getName() + ".install-" + UUID.randomUUID()
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Interrupted staging directories accumulate

If Android terminates the process after creating a UUID-suffixed .install-* directory, the finally cleanup never runs and restart recovery scans only .backup-* directories. These abandoned extractions can retain hundreds of megabytes each and consume application storage over time.

Knowledge Base Used: Plugin System

@UnschooledGamer UnschooledGamer added community and removed enhancement New feature or request labels Aug 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: Backlog

Development

Successfully merging this pull request may close these issues.

3 participants